Skip to content

Focus Visible Implementation ​

This document describes the implementation details for the Focus Visible test (S.C 2.4.7).

Scope ​

This test checks that a visible focus indicator exists on all elements focusable with Tab. It does not evaluate whether this indicator is clearly perceptible (i.e., whether there is sufficient contrast between the focused and unfocused states), nor does it check elements that cannot be reached by Tab alone.

Test Details ​

The test starts by focusing the first focusable element within the container selected by the tester.

While focus remains within that container and no previously visited element is encountered:

  1. A screenshot is taken of the focused element (screenshot details in the next section)
  2. Tab is pressed to move focus to the next element
  3. A second screenshot is taken of the element that was focused in step 1
  4. The differences between the screenshots from steps 1 and 3 are checked. If there are no differences, it is reported as a non-visible focus indicator (comparison details below).

Most important details: Two screenshots and one comparison are made per element. The number of visited elements matches the number of elements reachable by Tab within the container.

Screenshots ​

In order to prevent the environment from interfering with the comparison and to capture the element without excluding any borders or outlines, we insert the element into a new container called a wrapper. We clone the original element with all its styles and insert it into the wrapper. This wrapper will have the same background as the parent of the original element. Additionally, a 20px padding is added to the wrapper to ensure the entire outline of the element is captured.

The only style of the original element that is not cloned is the position. We cannot leave the element with position: absolute because it may not fall inside the wrapper.

Pages from Mastercard are excluded from this process because it is practically impossible to clone the styles of focused elements on these pages. On these pages, the screenshot is taken directly where the element is located (the element is not cloned or inserted into a wrapper).

Image Comparison ​

Once the two states (focused and unfocused) of the element have been captured, both captures are compared to determine whether the focus indicator is visible.

First, 5px are cropped from the image borders. Since the wrapper has a 20px padding, this does not affect the part of the image corresponding to the element, and at the same time removes areas of the image that may be influenced by the page environment.

Next, the difference image is calculated. This is the pixel-by-pixel subtraction between one image and the other. This difference image is then converted to grayscale to facilitate further processing.

In some cases, differences appear in images that look visually identical. This can be due to phenomena such as antialiasing, noise introduced during screenshot capture, and/or the browser's own rendering. This noise manifests in the difference image as non-black pixels (a black pixel in the difference image means that pixel has the same value in both compared images).

The strategy for removing noise is to try to eliminate all non-black shapes in the image that follow irregular patterns — that is, shapes that do not form long, straight lines or solid color bands.

This approach is currently not perfect, as it fails to detect non-visible focus on some elements. However, using a more aggressive noise removal strategy would begin to produce false positives on elements with a very subtle focus indicator.